Appearance
《面向对象程序设计(Java)》期末试卷A (精选02)
一、选择题(共 20 题,每题 1.5 分,共 30 分)
1.运行名为 Test.class 的 Java 字节码文件,所用的 Java 命令是( )
- A.
javac Test.java - B.
javac Test.class - C.
java Test - D.
java Test.class
查看答案与解析
答案:C
解析:
javac是编译命令,用于将.java编译为.class,A、B 错误。java是运行命令,运行时不加.class后缀,只需写类名。- 正确命令:
java Test,C 正确。
难度: ⭐ 考点: #Java命令 #javac #java
💡 学习锦囊
📖 相关公式与知识点:
- javac:编译器,将
.java源文件编译为.class字节码文件 - java:虚拟机启动命令,运行字节码文件(不加
.class后缀)
易错点
java Test运行时不加.class后缀javac后面跟源文件名(带.java),java后面跟类名(不带后缀)
🔄 举一反三
- 如果源文件名为
HelloWorld.java,编译和运行的命令分别是什么?查看练习答案与解析
答案:编译
javac HelloWorld.java,运行java HelloWorld解析:javac 编译源文件需要带.java后缀,java 运行类不需要后缀。
2.下列不可作为 Java 语言标识符的是( )
- A.
a2 - B.
$2 - C.
_2 - D.
22
查看答案与解析
答案:D
解析: Java 标识符规则:不能以数字开头,不能使用关键字。
- A:
a2以字母开头,合法 ✓ - B:
$2以 $ 开头,合法 ✓ - C:
_2以下划线开头,合法 ✓ - D:
22以数字开头,非法 ✗
难度: ⭐ 考点: #标识符 #命名规则
💡 学习锦囊
📖 相关公式与知识点:
- 标识符规则:字母/
_/$开头,不含关键字,区分大小写 - 不能以数字开头
易错点
$和_可以作为标识符开头- 数字不能作为标识符开头,但可以出现在中间
🔄 举一反三
- 下列哪个是合法的 Java 标识符?
- A.
class - B.
_name - C.
3abc - D.
my-name
查看练习答案与解析
答案:B 解析:A 是 Java 关键字;B 以下划线开头,合法;C 以数字开头,非法;D 含连字符,非法。
- A.
3.能正确表示 a 和 b 同时为正或同时为负的逻辑表达式是( )
- A.
(a >= 0 || b >= 0) && (a < 0 || b < 0) - B.
(a >= 0 && b >= 0) && (a < 0 && b < 0) - C.
a + b > 0 - D.
a * b > 0
查看答案与解析
答案:D
解析: "a 和 b 同时为正或同时为负"即 a 和 b 同号。
- A:表示"至少一个非负且至少一个负",即一正一负,错误。
- B:两个条件不可能同时成立,永远为 false,错误。
- C:两个负数相加仍为负,不能表示同号,错误。
- D:
a * b > 0→ 同号相乘为正,异号相乘为负,正确 ✓
难度: ⭐⭐ 考点: #逻辑表达式 #同号判断
💡 学习锦囊
📖 相关公式与知识点:
- 同号判断:
a * b > 0表示 a、b 同号(同正或同负) - 异号判断:
a * b < 0表示 a、b 异号
易错点
a * b > 0不能处理 a 或 b 为 0 的情况(0 乘任何数为 0)- 不能用
a + b > 0判断同号,因为一正一负也可能和为正
🔄 举一反三
- 表示"a、b 同时为正"的逻辑表达式是?
查看练习答案与解析
答案:
a > 0 && b > 0解析:同时为正需要两个条件都满足,用&&连接。
4.定义了 int 型二维数组 a[6][7] 后,数组元素 a[3][4] 前的数组元素个数为( )
- A. 28
- B. 25
- C. 18
- D. 17
查看答案与解析
答案:B
解析: Java 二维数组按行优先存储。a[3][4] 前的元素包括:
- 前 3 行完整元素:3 × 7 = 21 个
- 第 4 行前 4 个元素:4 个
- 总计:21 + 4 = 25 个
公式:行号 × 列数 + 列号 = 3 × 7 + 4 = 25
难度: ⭐⭐ 考点: #二维数组 #行优先存储
💡 学习锦囊
📖 相关公式与知识点:
- 二维数组元素计数:
a[m][n]前的元素个数 =m × 列数 + n - 行优先存储:先存完一行再存下一行
易错点
- 二维数组元素计数从 0 开始,
a[3][4]前有 25 个元素(不是 24) - 注意是"前面的元素个数",不包括自身
🔄 举一反三
- 定义
int a[5][6],a[2][3]前有多少个元素?查看练习答案与解析
答案:15 解析:2 × 6 + 3 = 15。前两行完整元素 12 个,第三行前 3 个元素。
5.对于下列代码:
String str1 = "java";
String str2 = "java";
String str3 = new String("java");
StringBuffer str4 = new StringBuffer("java");2
3
4
下列结论正确的是( )
- A.
str1 == str2 - B.
str1 == str4 - C.
str2 == str3 - D.
str3 == str4
查看答案与解析
答案:A
解析:
str1 = "java"和str2 = "java":字符串字面量在常量池中共享,str1 == str2为 true,A 正确。str3 = new String("java"):堆内存新对象,str2 == str3为 false,C 错误。str4 = new StringBuffer("java"):不同类型,==比较为 false,B、D 错误。
难度: ⭐⭐ 考点: #String比较 #字符串常量池 #==与equals
💡 学习锦囊
📖 相关公式与知识点:
- String 常量池:字面量共享同一对象,
new创建新对象 ==:比较引用(地址)equals():比较内容
易错点
new String("java")创建新对象,与常量池中的"java"地址不同==比较地址,equals()比较内容
🔄 举一反三
- 以下代码输出什么?java
String s1 = "hello"; String s2 = new String("hello"); System.out.println(s1 == s2); System.out.println(s1.equals(s2));1
2
3
4查看练习答案与解析
答案:
false然后true解析:s1在常量池中,s2在堆内存中,==比较地址不同所以 false;equals()比较内容相同所以 true。
6.关于接口,下面的叙述错误的是( )
- A. 一个接口可以继承多个接口
- B. 一个类可以实现多个接口
- C. 抽象类在实现接口时,可以不实现该接口中声明的所有方法
- D. 抽象类在实现接口时,必须实现该接口中声明的所有方法
查看答案与解析
答案:D
解析:
- A 正确:接口支持多继承。
- B 正确:类可实现多个接口。
- C 正确:抽象类实现接口时可不实现所有方法。
- D 错误:抽象类实现接口时不需要实现所有方法,未实现的保留为抽象方法。
难度: ⭐⭐ 考点: #接口 #抽象类 #多继承
💡 学习锦囊
📖 相关公式与知识点:
- 接口多继承:接口可以继承多个接口
- 类多实现:一个类可以实现多个接口
- 抽象类实现接口:可以只实现部分方法
易错点
- 抽象类实现接口时不需要实现所有方法
- 具体类(非抽象类)实现接口时必须实现所有方法
🔄 举一反三
- 如果一个具体类实现了一个接口但没有实现所有方法,会怎样?
查看练习答案与解析
答案:编译报错 解析:具体类必须实现接口中的所有抽象方法,否则必须声明为抽象类。
7.在某个类中存在一个方法:void getSort(int x),以下能作为这个方法的重载的声明的是( )
- A.
public get(float x) - B.
int getSort(int y) - C.
double getSort(int x, int y) - D.
void get(int x, int y) {}
查看答案与解析
答案:C
解析: 方法重载要求:方法名相同,参数列表不同。
- A:方法名不同(
get≠getSort),不是重载 ✗ - B:参数列表相同(形参名不同不算),仅返回类型不同,不是合法重载 ✗
- C:参数个数不同,合法重载 ✓
- D:方法名不同,不是重载 ✗
难度: ⭐⭐ 考点: #方法重载 #参数列表
💡 学习锦囊
📖 相关公式与知识点:
- 方法重载:同名不同参,与返回类型无关
- 形参名不同不算重载
易错点
- 方法重载只看参数列表,不看返回类型和形参名
- 仅返回类型不同不是合法重载
🔄 举一反三
- 下列哪个是
public void show(int a)的合法重载?- A.
public int show(int b) - B.
public void show(double a) - C.
public void show() - D. B 和 C 都合法
查看练习答案与解析
答案:D 解析:A 参数相同仅返回类型不同,不合法;B 参数类型不同,合法;C 参数个数不同,合法。
- A.
8.下面关于缺省构造方法的描述中正确的是( )
- A. 当类中没有定义任何构造方法时,Java 编译器将为这个类创建缺省构造方法
- B. 缺省构造方法可以初始化其他方法中定义的变量
- C. Java 编译器会为所有的类创建缺省构造方法
- D. 如果在一个类中定义的构造方法都声明了参数,Java 编译器将为这个类创建一个缺省构造方法
查看答案与解析
答案:A
解析:
- A 正确:无任何构造方法时,编译器自动生成缺省构造方法。
- B 错误:缺省构造方法不能初始化局部变量。
- C 错误:已有构造方法时不再生成缺省构造方法。
- D 错误:已有带参构造方法时不会生成缺省构造方法。
难度: ⭐⭐ 考点: #构造方法 #缺省构造方法
💡 学习锦囊
📖 相关公式与知识点:
- 缺省构造方法:无参,方法体为空
- 自动生成条件:类中没有定义任何构造方法时
易错点
- 如果自定义了构造方法,编译器不再提供默认无参构造
🔄 举一反三
- 如果类中定义了
MyClass(int x)但没有定义无参构造方法,new MyClass()能编译吗?查看练习答案与解析
答案:不能 解析:已定义有参构造方法后,编译器不再提供默认无参构造方法,需要手动添加。
9.下列哪个选项定义的抽象类是正确的?( )
- A.
public abstract final class Car { abstract void move(); } - B.
public class abstract Car { abstract void move(); } - C.
public abstract class Car { abstract void drive(); } - D.
public abstract class Car { abstract void drive() { } }
查看答案与解析
答案:C
解析:
- A:
abstract和final矛盾 ✗ - B:关键字顺序错误 ✗
- C:语法正确 ✓
- D:抽象方法不能有方法体 ✗
难度: ⭐⭐ 考点: #抽象类 #抽象方法 #final
💡 学习锦囊
📖 相关公式与知识点:
- 抽象类:用
abstract修饰 - 抽象方法:只有声明,没有方法体
- abstract 与 final:不能同时修饰类
易错点
- 抽象方法不能有方法体
{ } abstract和final不能同时修饰类
🔄 举一反三
- 下列哪个抽象方法定义是正确的?
- A.
abstract void show() { } - B.
abstract void show(); - C.
void abstract show(); - D.
abstract show();
查看练习答案与解析
答案:B 解析:A 有方法体,错误;B 无方法体,正确;C 关键字顺序错误;D 缺少返回类型。
- A.
10.在使用 super 和 this 关键字时,以下描述正确的是( )
- A. 在子类构造方法中使用
super()显示调用父类的构造方法,super()必须写在子类构造方法的第一行,否则编译不通过 - B.
this()和super()可以同时出现在一个构造函数中 - C.
super()和this()不一定要放在构造方法内第一行 - D.
this()和super()可以在 static 环境中使用,包括 static 方法和 static 语句块
查看答案与解析
答案:A
解析:
- A 正确:
super()必须在子类构造方法第一行。 - B 错误:
this()也必须在第一行,不能同时出现。 - C 错误:必须在第一行。
- D 错误:不能在 static 环境中使用。
难度: ⭐⭐ 考点: #super #this #构造方法
💡 学习锦囊
📖 相关公式与知识点:
- super():调用父类构造方法,必须在第一行
- this():调用本类其他构造方法,必须在第一行
- 互斥:
super()和this()不能同时出现
易错点
super()和this()必须在构造方法第一行- 两者不能同时出现在同一个构造方法中
🔄 举一反三
- 如果子类构造方法中没有显式调用
super(),会发生什么?查看练习答案与解析
答案:编译器自动在第一行添加
super(),调用父类的无参构造方法 解析:如果父类没有无参构造方法,编译报错。
11.以下程序段输出的结果是( )
public class Test {
public static void main(String args[]) {
String str = "ABCDE";
str.substring(3);
str.concat("XYZ");
System.out.print(str);
}
}2
3
4
5
6
7
8
- A.
DE - B.
DEXYZ - C.
ABCDE - D.
CDEXYZ
查看答案与解析
答案:C
解析: String 不可变,substring() 和 concat() 返回新字符串,不修改原字符串。
str.substring(3); // 返回 "DE",str 不变
str.concat("XYZ"); // 返回 "ABCDEXYZ",str 不变
System.out.print(str); // 输出 "ABCDE"2
3
难度: ⭐⭐ 考点: #String不可变 #substring #concat
💡 学习锦囊
📖 相关公式与知识点:
- String 不可变:
substring()、concat()等返回新对象,不修改原字符串 - 修改 String:需要赋值
str = str.substring(3);
易错点
- String 的所有修改方法都返回新对象,原对象不变
- 必须赋值才能改变引用
🔄 举一反三
- 以下代码输出什么?
String s = "hello"; s = s.toUpperCase(); System.out.println(s);查看练习答案与解析
答案:
HELLO解析:toUpperCase()返回新字符串,赋值给 s 后 s 指向新对象 "HELLO"。
12.如下程序输出( )
public class Test {
public static void main(String[] args) {
String s = "Java";
StringBuilder buffer = new StringBuilder(s);
change(s);
System.out.println(s);
}
private static void change(String s) {
s = s + " and HTML";
}
}2
3
4
5
6
7
8
9
10
11
- A.
Java - B.
Java and HTML - C.
and HTML - D. 什么都不显示
查看答案与解析
答案:A
解析: String 不可变,Java 值传递。change(s) 中修改局部变量 s 不影响 main 中的 s。输出 Java。
难度: ⭐⭐⭐ 考点: #值传递 #String不可变
💡 学习锦囊
📖 相关公式与知识点:
- Java 值传递:基本类型传值副本,引用类型传地址副本
- String 不可变:修改 String 会创建新对象
易错点
- Java 只有值传递,没有引用传递
- String 在方法内重新赋值不影响外部变量
🔄 举一反三
- 如果将
change方法的参数改为StringBuilder,输出什么?查看练习答案与解析
答案:
Java and HTML解析:StringBuilder 是可变对象,在方法内s.append(" and HTML")会修改原对象内容,外部变量能看到变化。
13.在 Java 中,关于 HashMap 类的描述,以下选项错误的是( )
- A. HashMap 使用键/值的形式保存数据
- B. HashMap 能够保证其中元素的顺序
- C. HashMap 允许将 null 用作键
- D. HashMap 允许将 null 用作值
查看答案与解析
答案:B
解析: HashMap 不保证元素顺序。如需有序使用 LinkedHashMap 或 TreeMap。
难度: ⭐⭐ 考点: #HashMap #集合框架
💡 学习锦囊
📖 相关公式与知识点:
- HashMap:基于哈希表,键值对存储,不保证顺序
- LinkedHashMap:保持插入顺序
- TreeMap:按键的自然顺序排序
易错点
- HashMap 不保证元素顺序
- 如需有序 Map,使用 LinkedHashMap 或 TreeMap
🔄 举一反三
- 如果需要按插入顺序遍历 Map,应该使用哪个类?
查看练习答案与解析
答案:LinkedHashMap 解析:LinkedHashMap 在 HashMap 基础上维护了双向链表,保持键值对的插入顺序。
14.给出下面代码,关于该程序以下哪个说法是正确的?( )
public class Person {
static int arr[] = new int[5];
public static void main(String a[]) {
System.out.println(arr[0]);
}
}2
3
4
5
6
- A. 编译时将产生错误
- B. 编译时正确,运行时将产生错误
- C. 输出零
- D. 输出空
查看答案与解析
答案:C
解析:static int arr[] = new int[5] 创建静态数组,int 类型默认值为 0。
- 数组在类加载时初始化,所有元素默认为 0。
arr[0]输出 0,不是 "空"(null)。
难度: ⭐⭐ 考点: #数组默认值 #静态数组
💡 学习锦囊
📖 相关公式与知识点:
- 数组默认值:int → 0, boolean → false, 对象 → null
- 静态数组:类加载时初始化
易错点
- 数组默认值是 0 不是 null(int 类型)
- 对象数组默认值是 null,基本类型数组默认值是 0/false
🔄 举一反三
String[] arr = new String[3];中arr[0]的值是什么?查看练习答案与解析
答案:
null解析:String 是对象类型,对象数组元素默认值为 null。
15.下面语句序列执行后,k 的值是什么?( )
int x = 6, y = 10, k = 5;
switch (x % y) {
case 0: k = x * y;
case 6: k = x / y;
case 12: k = x - y;
default: k = x * y - x;
}2
3
4
5
6
7
- A. 60
- B. 5
- C. 0
- D. 54
查看答案与解析
答案:D
解析:x % y = 6 % 10 = 6,匹配 case 6。无 break,贯穿执行:
case 6: k = 6 / 10 = 0
case 12: k = 6 - 10 = -4
default: k = 6 * 10 - 6 = 542
3
最终 k = 54。注意 k 初始值为 5,但贯穿执行后所有赋值都被覆盖。
难度: ⭐⭐⭐ 考点: #switch贯穿 #break #取模运算
💡 学习锦囊
📖 相关公式与知识点:
- switch 贯穿:没有 break 会从匹配 case 执行到底
- break:跳出 switch 语句
易错点
- switch 语句忘记写 break 导致贯穿
- 贯穿会从匹配的 case 开始执行所有后续 case 和 default
🔄 举一反三
- 如果每个 case 后都加上
break;,k 的值是什么?查看练习答案与解析
答案:0 解析:匹配 case 6,
k = 6 / 10 = 0,break 跳出 switch,k = 0。
16.以下关于运算符优先级顺序的描述中正确的是( )
- A. 关系运算符 < 算术运算符 < 赋值运算符
- B. 赋值运算符 < 关系运算符 < 算术运算符
- C. 算术运算符 < 关系运算符 < 赋值运算符
- D. 赋值运算符 < 算术运算符 < 关系运算符
查看答案与解析
答案:B
解析: Java 运算符优先级(从高到低):算术运算符 > 关系运算符 > 赋值运算符
用"<"表示"优先级低于":赋值运算符 < 关系运算符 < 算术运算符
难度: ⭐⭐ 考点: #运算符优先级
💡 学习锦囊
📖 相关公式与知识点:
- 运算符优先级:算术 > 关系 > 逻辑 > 赋值
- 赋值运算符优先级最低
易错点
- 赋值运算符优先级最低
- 逻辑运算符介于关系运算符和赋值运算符之间
🔄 举一反三
- 表达式
a + b > c && d == e中,运算符的执行顺序是?查看练习答案与解析
答案:先
a+b,再>c和==e,最后&&解析:算术(+) → 关系(>, ==) → 逻辑(&&),按优先级从高到低执行。
17.声明成员变量时,如果不使用任何访问控制符,则以下哪种类型的类不能对该成员进行直接访问( )
- A. 同一类
- B. 同一包中的子类
- C. 同一包中的非子类
- D. 不同包中的子类
查看答案与解析
答案:D
解析: 不使用任何访问控制符时,成员的访问权限为默认(包访问权限/default):
| 访问者 | 能否访问 |
|---|---|
| 同一类 | ✓ |
| 同一包中的子类 | ✓ |
| 同一包中的非子类 | ✓ |
| 不同包中的子类 | ✗ |
| 不同包中的非子类 | ✗ |
难度: ⭐ 考点: #访问修饰符 #default权限
💡 学习锦囊
📖 相关公式与知识点:
- private:本类可访问
- default(无修饰符):本包可访问
- protected:本包 + 其他包的子类可访问
- public:所有类可访问
易错点
- default 权限不允许跨包访问,即使是子类也不行
- protected 允许跨包的子类访问,这是与 default 的关键区别
🔄 举一反三
- 如果成员用
protected修饰,不同包中的子类能否访问?查看练习答案与解析
答案:能 解析:protected 允许同包的所有类和不同包的子类访问。
18.以下程序运行的结果是( )
public class Test {
public int div(int a, int b) {
try {
return a / b;
} catch (Exception e) {
System.out.println("Exception");
} catch (NULLException e) {
System.out.println("NullException");
} catch (ArithmeticException e) {
System.out.println("ArithmeticException");
} finally {
System.out.println("finally");
}
return 0;
}
public static void main(String[] args) {
Test demo = new Test();
System.out.println("商是:" + demo.div(9, 10));
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- A.
Exception finally 商是:0 - B.
ArithmeticException finally 商是:0 - C.
finally 商是:0 - D. 编译报错
查看答案与解析
答案:D
解析: Java 异常捕获要求:子类异常必须写在父类异常之前,否则编译报错。
代码中 catch(Exception e) 写在最前面,而 ArithmeticException 是 Exception 的子类,后面的 catch(ArithmeticException e) 永远不可达,编译器报错。
此外,NullException 不是 Java 标准异常类(标准类为 NullPointerException),也会导致编译错误。
难度: ⭐⭐⭐ 考点: #异常处理 #catch顺序 #编译错误
💡 学习锦囊
📖 相关公式与知识点:
- catch 顺序:子类异常必须在父类异常之前
- 不可达代码:如果父类异常在前,子类异常的 catch 块永远不可达
易错点
- 子类异常必须写在父类异常之前,否则编译报错
NullException不是 Java 标准异常类
🔄 举一反三
- 将 catch 顺序调整为
ArithmeticException→Exception,调用div(9, 0)输出什么?查看练习答案与解析
答案:
ArithmeticException然后finally然后商是:0解析:9/0 抛出 ArithmeticException,匹配第一个 catch 输出 "ArithmeticException",finally 始终执行,最后 return 0 输出 "商是:0"。
19.下列哪个流类属于面向字符的输入流( )
- A.
BufferedWriter - B.
FileInputStream - C.
ObjectInputStream - D.
InputStreamReader
查看答案与解析
答案:D
解析:
- A:
BufferedWriter是输出流 ✗ - B:
FileInputStream是字节流 ✗ - C:
ObjectInputStream是字节流 ✗ - D:
InputStreamReader是字节流到字符流的桥梁,属于面向字符的输入流 ✓
难度: ⭐⭐ 考点: #IO流 #字符流 #字节流
💡 学习锦囊
📖 相关公式与知识点:
- 字节流:InputStream/OutputStream 及其子类
- 字符流:Reader/Writer 及其子类
- InputStreamReader:字节流 → 字符流的桥梁
易错点
- FileInputStream 是字节流,不是字符流
- InputStreamReader 是字符输入流
🔄 举一反三
- 下列哪个是面向字节的输出流?
- A.
FileReader - B.
FileWriter - C.
FileOutputStream - D.
BufferedReader
查看练习答案与解析
答案:C 解析:A、D 是字符输入流;B 是字符输出流;C 是字节输出流。
- A.
20.下面关于 import 语句的说法,错误的是( )
- A. 编译器会搜索要 import 的类的编译结果而不是源文件
- B. import 是要告诉编译器要 import 的包的信息
- C. import 是要把 import 的类的源代码插入到 import 语句所在的地方
- D. 编译器在编译的时候需要能访问得到 import 的类的编译结果文件
查看答案与解析
答案:C
解析:
- A 正确:编译器查找的是
.class文件 - B 正确:
import语句告诉编译器所需类的包路径信息 - C 错误:
import不会将源代码插入到当前位置,它只是告诉编译器去哪里找类 - D 正确:编译时需要能访问到被 import 类的
.class文件
难度: ⭐⭐ 考点: #import #编译机制
💡 学习锦囊
📖 相关公式与知识点:
- import 作用:告诉编译器类的包路径,不是插入源代码
- 与 C 的 #include 区别:
#include是插入源代码,import是指明路径
易错点
- Java 的
import不等于 C 语言的#include import不会将源代码插入到当前位置
🔄 举一反三
import java.util.*;的作用是什么?查看练习答案与解析
答案:告诉编译器在
java.util包中查找所需的类 解析:通配符*表示导入包中所有类,但不会实际加载所有类,只在需要时查找。
二、程序阅读及填空题(共 20 空,前 16 空每空 2 分,第 17-18 空每空 4 分,共 40 分)
1. 请在空格中填写以下程序执行的结果:
public class IdentifyMyParts {
public static int x = 7;
public int y = 3;
}
public class Test {
public static void main(String[] args) {
IdentifyMyParts a = new IdentifyMyParts();
IdentifyMyParts b = new IdentifyMyParts();
a.y = 5;
b.y = 6;
a.x = 1;
b.x = 2;
System.out.println("a.y = " + a.y); // 屏幕输出为 (1)
System.out.println("b.y = " + b.y); // 屏幕输出为 (2)
System.out.println("a.x = " + a.x); // 屏幕输出为 (3)
System.out.println("b.x = " + b.x); // 屏幕输出为 (4)
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
查看答案与解析
(1) 答案:a.y = 5
(2) 答案:b.y = 6
(3) 答案:a.x = 2
(4) 答案:b.x = 2
解析:
y是实例变量,每个对象独立:a.y = 5,b.y = 6。x是静态变量,所有对象共享:最终x = 2,a.x和b.x都是 2。
难度: ⭐⭐ 考点: #静态变量 #实例变量 #static
💡 学习锦囊
📖 相关公式与知识点:
- 静态变量 vs 实例变量:静态变量被所有实例共享,实例变量每个对象独立
易错点
- 静态变量被所有实例共享,修改一个对象会影响所有对象
🔄 举一反三
- 以下代码输出什么?java
public class Test { static int count = 0; int id; Test() { count++; id = count; } public static void main(String[] args) { Test t1 = new Test(); Test t2 = new Test(); Test t3 = new Test(); System.out.println("t1.id=" + t1.id + ", t2.id=" + t2.id + ", count=" + count); } }1
2
3
4
5
6
7
8
9
10
11查看练习答案与解析
答案:
t1.id=1, t2.id=2, count=3解析:每次创建对象 count 自增并赋给 id。最终 count=3。
2. 请在空格中填写以下程序执行的结果:
public class java1 {
public static void main(String[] args) {
int arr[][] = {{34, 21, 45, 67, 20},
{23, 10, 3, 45, 76},
{22, 3, 79, 56, 50}};
int i = 0;
int j = 0;
int min = arr[0][0];
while (i < 3) {
while ( (5) ) {
if (arr[i][j] < min)
min = arr[i][j];
j++;
}
(6) ;
(7) ;
}
System.out.println("The min:" + min);
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
查看答案与解析
(5) 答案:j < 5
(6) 答案:i++
(7) 答案:j = 0
解析: 遍历二维数组找最小值。内层循环 j < 5(5 列),外层 i++,列索引重置 j = 0。最小值为 3。
难度: ⭐⭐ 考点: #二维数组 #数组遍历 #求最小值
💡 学习锦囊
📖 相关公式与知识点:
- 二维数组遍历:外层循环行,内层循环列,注意索引重置
易错点
- 内层循环结束后必须重置列索引 j = 0
- 行索引 i++ 不能遗漏
🔄 举一反三
- 如何修改程序使其求最大值?
查看练习答案与解析
将
if (arr[i][j] < min)改为if (arr[i][j] > max),并将初始值设为int max = arr[0][0]。 解析:只需将比较方向反转即可。
3. 写出下列程序的输出结果:
class Exam31 {
public static void main(String[] args) {
int i = 1, j = 2, k = 3;
boolean b = -i > 1 && j++ < 2 && k-- < 3;
b = ++i > 1 || j-- < 2 && --k > 3;
b = ++i > 1 | j-- < 2 && --k > 3;
System.out.println("i=" + i); // 屏幕输出为 (8)
System.out.println("j=" + j); // 屏幕输出为 (9)
System.out.println("k=" + k); // 屏幕输出为 (10)
}
}2
3
4
5
6
7
8
9
10
11
查看答案与解析
(8) 答案:i=3
(9) 答案:j=1
(10) 答案:k=3
解析:
第一行:b = -i > 1 && j++ < 2 && k-- < 3;
-i= -1,-1 > 1为 false,&&短路- i=1, j=2, k=3
第二行:b = ++i > 1 || j-- < 2 && --k > 3;
++i→ i=2,2 > 1为 true,||短路- i=2, j=2, k=3
第三行:b = ++i > 1 | j-- < 2 && --k > 3;
- 等价于:
(++i > 1) | ((j-- < 2) && (--k > 3)) ++i→ i=3,3 > 1为 true|非短路或,右边也要计算j-- < 2:j=2,2 < 2为 false,j=1false && ...短路,--k不执行- i=3, j=1, k=3
难度: ⭐⭐⭐ 考点: #短路逻辑 #非短路或 #自增自减
💡 学习锦囊
📖 相关公式与知识点:
- 短路逻辑运算符:
&&和||会短路 - 非短路逻辑运算符:
&和|不会短路,两边都计算
易错点
|是非短路或,两边都会计算;||是短路或&&短路:左边为 false 不执行右边
🔄 举一反三
- 以下代码输出什么?java
int a = 5, b = 10; boolean r = (a > 3) | (b++ > 10); System.out.println("b=" + b);1
2
3查看练习答案与解析
答案:
b=11解析:|是非短路或,即使左边为 true,右边b++ > 10仍然执行,b 从 10 变为 11。
4. 如果一个整数 a 能被数 b 整除,则 b 是 a 的因子。下面是一个 Java 程序,它的功能是求给定整数 n 的所有因子的和 sum,请将程序补充完整。
public class Test {
public static void main(String args[]) {
int n = 6, sum = 0;
for (int k = 1; (11) ; k++) {
if ( (12) )
sum += k;
}
System.out.println(sum);
}
}2
3
4
5
6
7
8
9
10
查看答案与解析
(11) 答案:k <= n
(12) 答案:n % k == 0
解析: 求 n 的因子之和。循环 k <= n,判断因子 n % k == 0。n=6 时,sum = 1+2+3+6 = 12。
难度: ⭐⭐ 考点: #因子 #取模运算 #循环
💡 学习锦囊
📖 相关公式与知识点:
- 因子:如果
n % k == 0,则 k 是 n 的因子 - 取模运算:
n % k求 n 除以 k 的余数
易错点
- 循环条件是
k <= n,不是k < n(n 本身也是因子) - 判断因子用
n % k == 0,不是k % n == 0
🔄 举一反三
- 求 n=12 的所有因子之和。
查看练习答案与解析
答案:28 解析:12 的因子为 1, 2, 3, 4, 6, 12,sum = 28。
5. 以下程序为类 Animal、Bird、Parrot 和 Test 的代码,请将程序填充完整,并填写输出结果。包结构为:com.an 中有 Animal 类;com.bd 中有 Bird、Parrot、Test 类。
// com.an 包
package com.an;
public (13) class Animal {
(14) String color;
public abstract void sound();
public Animal() {
color = "";
System.out.println("动物");
}
}
// com.bd 包
package com.bd;
public class Bird (15) Animal {
public Bird() {
color = "绿色";
System.out.println("鸟");
}
public void sound() {
System.out.println("鸟叫声");
}
public void sound(String msg) {
System.out.println("鸟发出的叫声:" + msg);
}
}
public class Parrot extends Bird {
public Parrot() {
System.out.println("鹦鹉");
}
public void sound() {
(16) ; // 调用父类的方法 sound()
}
}
public class Test {
public static void main(String[] args) {
Parrot p = new Parrot();
p.sound();
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
查看答案与解析
(13) 答案:abstract
(14) 答案:protected
(15) 答案:extends
(16) 答案:super.sound()
解析:
- (13) Animal 有抽象方法,必须声明为
abstract。 - (14)
color需被不同包的子类访问,用protected。 - (15) Bird 继承 Animal,用
extends。 - (16) 调用父类方法用
super.sound()。
运行输出:动物 → 鸟 → 鹦鹉 → 鸟叫声
难度: ⭐⭐⭐ 考点: #抽象类 #继承 #protected #super #构造方法调用链
💡 学习锦囊
📖 相关公式与知识点:
- 抽象类:含抽象方法的类必须声明为
abstract protected访问权限:允许跨包的子类访问- 构造方法调用链:创建子类对象时,先调用父类构造方法
易错点
- 不同包的子类访问父类成员需要
protected或public权限 - 构造方法调用顺序:父类先于子类
🔄 举一反三
- 如果将
color改为private,Bird 类能否访问?查看练习答案与解析
答案:不能 解析:private 成员只能在本类中访问,即使是子类也不能直接访问。
6. 阅读以下程序,填写输出结果:
class A {
static int i;
int j, k;
public void fun() {
for (int j = 0; j < 10; j++) i++;
k++;
}
public void f() {
fun();
}
}
class B extends A {
static int k;
public void fun() {
super.fun();
for (j = 0; j < 20; j++) {
k += 3;
}
}
}
public class Main {
public static void main(String[] args) {
A a = new B();
a.f();
System.out.println(a.k); // 屏幕输出为 (17)
System.out.println(a.j); // 屏幕输出为 (18)
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
查看答案与解析
(17) 答案:1
(18) 答案:20
解析:
第一步:理解类结构
- A 有:
static int i(类变量)、int j, k(实例变量) - B 有:
static int k(类变量,隐藏了 A 的实例变量 k)、继承了 A 的实例变量 j
第二步:执行流程
A a = new B()— 创建 B 对象,编译时类型为 Aa.f()— 调用 A 的f(),其中调用fun()- 由于多态,
fun()调用 B 的fun()
第三步:B 的 fun() 执行
super.fun()→ A 的fun():for (int j = 0; j < 10; j++) i++;— 局部变量 j,A.i += 10 → i = 10k++;— A 的实例 k += 1 → A 的实例 k = 1
for (j = 0; j < 20; j++) { k += 3; }— B 的上下文:j是 A 的实例变量 j(继承),循环后 j = 20k是 B 的静态 k,k += 3 × 20 = 60 → B.k = 60
第四步:输出
a.k:编译时类型为 A,访问 A 的实例 k = 1(不是 B 的静态 k)a.j:A 的实例 j = 20
难度: ⭐⭐⭐ 考点: #静态变量 #实例变量 #多态 #变量隐藏 #编译时类型
💡 学习锦囊
📖 相关公式与知识点:
- 变量隐藏:子类声明与父类同名的静态变量,隐藏父类变量(不是重写)
- 多态:父类引用指向子类对象,方法调用看运行时类型,字段访问看编译时类型
- 字段访问规则:通过引用访问字段时,看编译时类型,不是运行时类型
易错点
- 子类静态变量隐藏父类实例变量,通过父类引用访问的是父类的字段
for (int j = 0; ...)中int j是局部变量,不影响实例变量 jfor (j = 0; ...)中j是实例变量,会修改- 多态只适用于方法,不适用于字段
🔄 举一反三
- 以下代码输出什么?java
class Parent { int x = 1; void print() { System.out.println("Parent x=" + x); } } class Child extends Parent { int x = 2; void print() { System.out.println("Child x=" + x); } } Parent p = new Child(); System.out.println(p.x); p.print();1
2
3
4
5查看练习答案与解析
答案:
1然后Child x=2解析:p.x访问字段,看编译时类型 Parent,所以是 1。p.print()是方法调用,看运行时类型 Child,输出 "Child x=2"。 - 如果将 B 中的
static int k改为int k(实例变量),a.k输出什么?查看练习答案与解析
答案:1 解析:即使 B 声明了实例变量 k 隐藏 A 的实例 k,通过 A 类型引用
a.k仍访问 A 的实例 k = 1。字段访问始终看编译时类型。
三、编程题(共 3 题,第 1 题 10 分,第 2 和 3 题各 15 分,共 40 分)
1. 定义基类 Point(点)和派生类 Circle(圆),求圆的周长。Point 类有两个私有的数据成员 float x, y;Circle 类新增一个私有的数据成员半径 float r 和一个公有的求周长的函数 getCircumference();请编写 Point 和 Circle 类以及测试类。
查看答案与解析
答案:
class Point {
private float x;
private float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
}
class Circle extends Point {
private float r;
public Circle(float x, float y, float r) {
super(x, y);
this.r = r;
}
public float getCircumference() {
return 2 * 3.14159f * r;
}
}
public class Test {
public static void main(String[] args) {
Circle c = new Circle(0, 0, 5);
System.out.println("圆心:(" + c.getX() + ", " + c.getY() + ")");
System.out.println("半径:" + 5);
System.out.println("周长:" + c.getCircumference());
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
运行结果:
圆心:(0.0, 0.0)
半径:5
周长:31.41592
3
解析:
第一步:设计 Point 类
- 两个私有成员
float x, y表示坐标 - 构造方法初始化坐标
- 提供
getX()和getY()方法供子类访问
第二步:设计 Circle 类
- 继承 Point 类,新增私有成员
float r表示半径 - 构造方法调用
super(x, y)初始化父类坐标 getCircumference()方法计算周长:$C = 2\pi r$
第三步:测试类
- 创建 Circle 对象,调用
getCircumference()输出周长
难度: ⭐⭐⭐ 考点: #继承 #super #圆的周长
💡 学习锦囊
📖 相关公式与知识点:
- 圆的周长:$C = 2\pi r$
- 继承:子类使用
extends继承父类,super()调用父类构造方法
易错点
- 子类构造方法必须调用
super(),且必须是第一条语句 - 父类的私有成员子类不能直接访问
🔄 举一反三
- 在此基础上,为 Circle 类添加求面积的方法
getArea()。查看练习答案与解析
javapublic float getArea() { return 3.14159f * r * r; }1
2
3解析:圆面积公式 $S = \pi r^2$,当 r=5 时,面积约为 78.53975。
2. 编写一个完整的 Java Application 程序。包含类 Shape、类 Oval、类 Main,具体要求如下:
(1)编写一个抽象类 Shape 表示形状对象,包含以下成员:
- 属性:double 型常量 PI,其值为 3.1415926;
- 方法:包含
double area()和double perimeter()两个抽象方法;
(2)编写一个 Shape 类的子类 Oval,表示椭圆对象,包含以下成员:
- 属性:double 型私有成员变量 a,表示长轴半径;double 型私有成员变量 b,表示短轴半径;
- 方法:构造方法
Oval(double a, double b),用参数设置椭圆的长轴半径和短轴半径;构造方法Oval(),将椭圆的长轴半径和短轴半径都初始化为 0;成员方法double area(),重写 Shape 类中的 area 方法,返回椭圆的面积;成员方法double perimeter(),重写 Shape 类中的 perimeter 方法,返回椭圆的周长;方法public String toString(),将当前的椭圆对象转换成字符串形式,例如长轴半径为 10.0,短轴半径为 5,返回字符串"Oval(a:10.0, b:5.0)"。
(3)编写公共类 Main,实现如下功能: 1)创建一个长轴半径为 8.0,短轴半径为 6.0 的椭圆对象; 2)分别用 area 和 perimeter 方法,求出以上椭圆的面积和周长并输出,输出过程中要求使用到 toString 方法。
查看答案与解析
答案:
abstract class Shape {
final double PI = 3.1415926;
abstract double area();
abstract double perimeter();
}
class Oval extends Shape {
private double a;
private double b;
public Oval(double a, double b) {
this.a = a;
this.b = b;
}
public Oval() {
this.a = 0;
this.b = 0;
}
@Override
double area() {
return PI * a * b;
}
@Override
double perimeter() {
return 2 * PI * Math.sqrt((a * a + b * b) / 2);
}
@Override
public String toString() {
return "Oval(a:" + a + ", b:" + b + ")";
}
}
public class Main {
public static void main(String[] args) {
Oval oval = new Oval(8.0, 6.0);
System.out.println(oval.toString() + " 的面积:" + oval.area());
System.out.println(oval.toString() + " 的周长:" + oval.perimeter());
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
运行结果:
Oval(a:8.0, b:6.0) 的面积:150.7964448
Oval(a:8.0, b:6.0) 的周长:44.4248084394470552
解析:
第一步:设计 Shape 抽象类
- 定义常量
PI = 3.1415926 - 声明两个抽象方法
area()和perimeter()
第二步:设计 Oval 类
- 私有成员
a(长轴半径)和b(短轴半径) - 两个构造方法:有参和无参
- 椭圆面积公式:$S = \pi \times a \times b$
- 椭圆周长近似公式:$P \approx 2\pi\sqrt{\frac{a^2 + b^2}{2}}$
toString()方法按指定格式返回字符串
第三步:Main 类测试
- 创建
Oval(8.0, 6.0)对象 - 使用
toString()输出对象信息 - 调用
area()和perimeter()计算面积和周长
难度: ⭐⭐⭐ 考点: #抽象类 #方法重写 #椭圆面积 #椭圆周长 #toString
💡 学习锦囊
📖 相关公式与知识点:
- 椭圆面积:$S = \pi \times a \times b$(a 为长轴半径,b 为短轴半径)
- 椭圆周长近似:$P \approx 2\pi\sqrt{\frac{a^2 + b^2}{2}}$
- 抽象类:含抽象方法的类必须声明为
abstract,子类必须实现所有抽象方法 - toString():Object 类的方法,子类重写以返回对象的字符串表示
易错点
- 子类构造方法必须调用
super(),且必须是第一条语句 - 抽象类不能实例化,但可以有构造方法供子类调用
toString()方法必须用public修饰final常量在声明时必须初始化- 椭圆周长没有精确的初等公式,常用近似公式
🔄 举一反三
- 如果要添加一个 Circle 类(圆是椭圆的特殊情况,a=b=r),如何实现?
查看练习答案与解析
javaclass Circle extends Oval { public Circle(double r) { super(r, r); } }1
2
3
4
5解析:圆是长轴半径等于短轴半径的椭圆,继承 Oval 并在构造方法中传入相同的 r 值即可。
3. 职工排序问题,要求使用线性表实现:
(1)为某保险公司设计一个职工管理系统,其中职工类的属性有:职工编号,姓名,性别,团保险业绩,个体验业绩;方法有:每个属性对应的 set、get 方法;不带参数的构造方法;带参数的构造方法,完成对职工属性的初始化;该类实现接口 Comparable,完成对职工总业绩的比较。
(2)设计一个类,实现 Comparator 接口,完成对团险业绩的比较;
(3)在 Main 类中,创建一个职工的线性表,分别完成对职工线性表按照总业绩升序排序,按照团险业绩升序排序。注意:不要设计键盘输入职工信息,可根据样例中提供的数据直接创建职工对象:
输入样例:
| 编号 | 团险 | 个险 | 姓名 | 性别 |
|---|---|---|---|---|
| 1 | 500 | 400 | 职工1 | female |
| 3 | 600 | 300 | 职工3 | male |
| 2 | 400 | 600 | 职工2 | female |
| 4 | 800 | 200 | 职工4 | female |
| 5 | 500 | 700 | 职工5 | male |
查看答案与解析
答案:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Employee implements Comparable<Employee> {
private int id;
private String name;
private String gender;
private double groupInsurance;
private double individualInsurance;
public Employee() {}
public Employee(int id, double groupInsurance, double individualInsurance,
String name, String gender) {
this.id = id;
this.groupInsurance = groupInsurance;
this.individualInsurance = individualInsurance;
this.name = name;
this.gender = gender;
}
public int getId() { return id; }
public void setId(int id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getGender() { return gender; }
public void setGender(String gender) { this.gender = gender; }
public double getGroupInsurance() { return groupInsurance; }
public void setGroupInsurance(double groupInsurance) { this.groupInsurance = groupInsurance; }
public double getIndividualInsurance() { return individualInsurance; }
public void setIndividualInsurance(double individualInsurance) { this.individualInsurance = individualInsurance; }
public double getTotalPerformance() {
return groupInsurance + individualInsurance;
}
@Override
public int compareTo(Employee other) {
return Double.compare(this.getTotalPerformance(), other.getTotalPerformance());
}
@Override
public String toString() {
return "编号:" + id + ", 姓名:" + name + ", 性别:" + gender
+ ", 团险:" + groupInsurance + ", 个险:" + individualInsurance
+ ", 总业绩:" + getTotalPerformance();
}
}
class GroupInsuranceComparator implements Comparator<Employee> {
@Override
public int compare(Employee e1, Employee e2) {
return Double.compare(e1.getGroupInsurance(), e2.getGroupInsurance());
}
}
public class Main {
public static void main(String[] args) {
List<Employee> list = new ArrayList<>();
list.add(new Employee(1, 500, 400, "职工1", "female"));
list.add(new Employee(3, 600, 300, "职工3", "male"));
list.add(new Employee(2, 400, 600, "职工2", "female"));
list.add(new Employee(4, 800, 200, "职工4", "female"));
list.add(new Employee(5, 500, 700, "职工5", "male"));
System.out.println("按总业绩升序排序:");
Collections.sort(list);
for (Employee e : list) {
System.out.println(e);
}
System.out.println("\n按团险业绩升序排序:");
Collections.sort(list, new GroupInsuranceComparator());
for (Employee e : list) {
System.out.println(e);
}
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
运行结果:
按总业绩升序排序:
编号:4, 姓名:职工4, 性别:female, 团险:800.0, 个险:200.0, 总业绩:1000.0
编号:1, 姓名:职工1, 性别:female, 团险:500.0, 个险:400.0, 总业绩:900.0
编号:3, 姓名:职工3, 性别:male, 团险:600.0, 个险:300.0, 总业绩:900.0
编号:2, 姓名:职工2, 性别:female, 团险:400.0, 个险:600.0, 总业绩:1000.0
编号:5, 姓名:职工5, 性别:male, 团险:500.0, 个险:700.0, 总业绩:1200.0
按团险业绩升序排序:
编号:2, 姓名:职工2, 性别:female, 团险:400.0, 个险:600.0, 总业绩:1000.0
编号:1, 姓名:职工1, 性别:female, 团险:500.0, 个险:400.0, 总业绩:900.0
编号:5, 姓名:职工5, 性别:male, 团险:500.0, 个险:700.0, 总业绩:1200.0
编号:3, 姓名:职工3, 性别:male, 团险:600.0, 个险:300.0, 总业绩:900.0
编号:4, 姓名:职工4, 性别:female, 团险:800.0, 个险:200.0, 总业绩:1000.02
3
4
5
6
7
8
9
10
11
12
13
解析:
第一步:设计 Employee 类
- 属性:id, name, gender, groupInsurance, individualInsurance
- 实现
Comparable<Employee>接口,compareTo()按总业绩比较 getTotalPerformance()计算总业绩 = 团险 + 个险
第二步:设计 GroupInsuranceComparator 类
- 实现
Comparator<Employee>接口 compare()方法按团险业绩比较
第三步:Main 类
- 使用
ArrayList创建职工线性表 Collections.sort(list)按总业绩排序(使用 Comparable)Collections.sort(list, comparator)按团险业绩排序(使用 Comparator)
难度: ⭐⭐⭐ 考点: #Comparable #Comparator #Collections.sort #ArrayList #泛型 #方法重写 #toString
💡 学习锦囊
📖 相关公式与知识点:
- Comparable 接口:
compareTo()方法定义自然排序,Collections.sort(list)使用 - Comparator 接口:
compare()方法定义定制排序,Collections.sort(list, comparator)使用 - ArrayList:动态数组,实现 List 接口
- Collections.sort():可传入 Comparator 定制排序规则
易错点
Comparable的compareTo()和Comparator的compare()方法签名不同- 浮点数比较不要用减法(可能有精度问题),用
Double.compare() Collections.sort()是静态方法,不是 List 的实例方法- Employee 类必须实现
Comparable<Employee>(带泛型),否则Collections.sort(list)无法编译
🔄 举一反三
- 如何按总业绩降序排序?
查看练习答案与解析
修改
compareTo()方法:java@Override public int compareTo(Employee other) { return Double.compare(other.getTotalPerformance(), this.getTotalPerformance()); }1
2
3
4解析:将比较顺序反转即可实现降序。或者使用
Collections.reverseOrder()。 - 使用 Lambda 表达式实现按个险业绩排序。
查看练习答案与解析
javaCollections.sort(list, (e1, e2) -> Double.compare(e1.getIndividualInsurance(), e2.getIndividualInsurance()));1解析:Lambda 表达式替代 Comparator 实现类,更简洁。
e1和e2是 Employee 对象,按个险业绩升序比较。